/******************************************************************************
 * This file is part of libemb.
 *
 * libemb is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * libemb is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with libemb.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Project: Embedme
 * Author : FergusZeng
 * Email  : cblock@126.com
 * git	  : https://git.oschina.net/cblock/embedme
 * Copyright 2014-2020 @ ShenZhen ,China
*******************************************************************************/
#include "Tracer.h"
#include "Pollset.h"
#include "DateTime.h"
#include "stdlib.h"

#define EPOLL_EVENT_MAX		2000

/****************************************************************************** 
 * EPOLL事件标志:
 * EPOLLIN : 输入事件,有数据待读取
 * EPOLLOUT: 输出事件,有数据要写入
 * EPOLLET : 边沿触发(事件就绪时,假设对事件没做处理,内核不会反复通知事件就绪)
 *           默认为水平触发(事件就绪时,假设对事件没做处理,内核会反复通知事件就绪)
 *****************************************************************************/
namespace libemb{

Pollset::Pollset()
{
}

Pollset::~Pollset()
{
	close();
}

bool Pollset::open(int maxEvents,bool et)
{
	if (maxEvents<=0)
	{
		return false;
	}
	m_size = maxEvents;
	if (m_epfd>0)
	{
		return true;
	}
	m_epfd = epoll_create1(0);
	if (m_epfd<0)
	{
		return false;
	}
	m_events = (struct epoll_event*)calloc(maxEvents,sizeof(struct epoll_event));
	if (m_events==NULL)
	{
		return false;
	}
	m_edgeTrig = et;
	return true;
}
void Pollset::close()
{
	if (m_epfd>0)
	{
		::close(m_epfd);
		m_epfd = -1;
	}
	if (m_events!=NULL)
	{
		free(m_events);
		m_events=NULL;
	}
}
bool Pollset::addEvent(int fd, int event)
{
	if (m_epfd<0)
	{
		return false;
	}
	struct epoll_event evt;
	evt.data.fd = fd;
	switch(event){
	case EventRead:
		evt.events = EPOLLIN;
		break;
	case EventWrite:
		evt.events = EPOLLOUT;
		break;
	default:
		return false;
	}
	if (m_edgeTrig)
	{
		evt.events |= EPOLLET;	
	}
	if(epoll_ctl(m_epfd,EPOLL_CTL_ADD,fd,&evt)<0)
	{
		return false;
	}
	return true;
}

bool Pollset::removeEvent(int fd, int event)
{
	if (m_epfd<0)
	{
		return false;
	}
	struct epoll_event evt;
	evt.data.fd = fd;
	switch(event){
	case EventRead:
		evt.events = EPOLLIN;
		break;
	case EventWrite:
		evt.events = EPOLLOUT;
		break;
	default:
		return false;
	}
	if (m_edgeTrig)
	{
		evt.events |= EPOLLET;	
	}
	if(epoll_ctl(m_epfd,EPOLL_CTL_DEL,fd,&evt)<0)
	{
		return false;
	}
	return true;
}

int Pollset::waitEvent(IntArray& fdArray, IntArray& evArray,int msTimeout)
{
	if (m_epfd<0)
	{
		return RC_ERROR;
	}
	int fds = epoll_wait(m_epfd,m_events,m_size,msTimeout);
	if (fds<0)
	{
		return RC_ERROR;
	}
	if (fds==0)
	{
		return RC_TIMEOUT;
	}
	evArray.clear();
	fdArray.clear();
	for(auto i=0; i<fds; i++)
	{
		int fd = m_events[i].data.fd;
		int event = -1;
		if(m_events[i].events &EPOLLIN)
		{
			event = EventRead;
		}
		else if (m_events[i].events &EPOLLOUT)
		{
			event = EventWrite;
		}
		if (event!=-1)
		{
			evArray.append(event);
			fdArray.append(fd);
		}
	}
	return RC_OK;
}
}

